I use the spaero::get_stats() function to calculate EWS over a moving window for the measles case incidence data from four cities in Niger. I use a bandwidth of 35 weeks, resulting in a 70 week window. All EWS are calculated with backward_only = TRUE so that EWS are only calculated based on data in the past.

# Load packages -----------------------------------------------------------
library(tidyverse)
library(spaero)
# Load data ---------------------------------------------------------------
fname <- "../../data/clean-data/weekly-measles-incidence-niger-cities-clean.RDS"
measles_data <- readRDS(fname) %>%
  filter(year > 1994)  # drop first NA year, only used for modeling
# Calculate EWS -----------------------------------------------------------
all_stats <- tibble()  # empty tibble for storage
for(do_region in unique(measles_data$region)){
  
  cases <- measles_data %>%
    filter(region == do_region) %>%
    pull(cases)
  
  city_stats <- spaero::get_stats(
    x = cases,
    center_trend = "local_constant", 
    center_kernel = "uniform", 
    center_bandwidth = 35, 
    stat_trend = "local_constant", 
    stat_kernel = "uniform", 
    stat_bandwidth = 35, 
    lag = 1, 
    backward_only = TRUE
  )$stats
  
  city_stats_tb <- as_tibble(city_stats) %>%
    mutate(
      time_iter = 1:n(),
      date = unique(measles_data$date)
    ) %>%
    gather(key = ews, value = value, -time_iter, -date) %>%
    mutate(region = do_region)
  
  all_stats <- bind_rows(all_stats, city_stats_tb)
}

Define a function to plot the EWS and the observations.

plot_it <- function(dates, cases, ews, lab, title){
  par(mar = c(5,5,2,5))
  plot(dates, cases, type = "l", main = title)
  par(new = T)
  plot(dates, ews, type = "l", col = "red", axes=F, xlab=NA, ylab=NA)
  axis(side = 4)
  mtext(side = 4, line = 3, lab, cex = 0.7)
}

Agadez

do_region <- "Agadez (City)"
dates <- unique(measles_data$date) 
par(mfrow = c(4,3))
for(do_ews in unique(all_stats$ews)){
  cases <- filter(measles_data, region == do_region) %>% pull(cases)
  ews <- filter(all_stats, region == do_region & ews == do_ews) %>% pull(value)
  plot_it(dates, cases, ews, lab = "ews", title = do_ews)
}

Maradi

do_region <- "Maradi (City)"
dates <- unique(measles_data$date) 
par(mfrow = c(4,3))
for(do_ews in unique(all_stats$ews)){
  cases <- filter(measles_data, region == do_region) %>% pull(cases)
  ews <- filter(all_stats, region == do_region & ews == do_ews) %>% pull(value)
  plot_it(dates, cases, ews, lab = "ews", title = do_ews)
}

Niamey

do_region <- "Niamey (City)"
dates <- unique(measles_data$date) 
par(mfrow = c(4,3))
for(do_ews in unique(all_stats$ews)){
  cases <- filter(measles_data, region == do_region) %>% pull(cases)
  ews <- filter(all_stats, region == do_region & ews == do_ews) %>% pull(value)
  plot_it(dates, cases, ews, lab = "ews", title = do_ews)
}

Zinder

do_region <- "Zinder (City)"
dates <- unique(measles_data$date) 
par(mfrow = c(4,3))
for(do_ews in unique(all_stats$ews)){
  cases <- filter(measles_data, region == do_region) %>% pull(cases)
  ews <- filter(all_stats, region == do_region & ews == do_ews) %>% pull(value)
  plot_it(dates, cases, ews, lab = "ews", title = do_ews)
}

Post-outbreak EWS

As the plots above show, most EWS get swamped out by the “memory” of outbreaks. To try and get around this, I am going to look at EWS after resetting them following outbreaks. I don’t think this is too contrived because real-world early detection systems would have to do the same thing.

How to ID outbreaks

Following Toby’s suggestion, I am going to look at the distribution of outbreak sizes (i.e., cumulative cases in each year). I can then fit a mixture distribution of two weighted exponentials to the data, with one exponential explaining the large outbreaks and one the small outbreaks. This provides an objective way to define outbreaks for EWS detection.

pop_data <- tibble(
  region = c("Agadez (City)", "Maradi (City)", "Niamey (City)", "Zinder (City)"),
  pop = c(118224, 267249, 1027000, 322935)
)
cusum_data <- measles_data %>%
  group_by(region, year) %>%
  summarise(cases = sum(cases)) %>%
  left_join(pop_data)
Joining, by = "region"
ggplot(cusum_data, aes(x = cases/pop)) +
  geom_histogram(aes(fill = region))

cusum_cases <- sort(cusum_data$cases / cusum_data$pop)
fit1 <- MASS::fitdistr(cusum_cases, "exponential") 
x <- cusum_cases
mixexplik <- function(p,lambda1,lambda2) {
  z <- p*dexp(x,lambda1) + (1-p)*dexp(x,lambda2)
  return(-sum(log(z)))
}
mle_fit <- suppressWarnings(  # ignore warnings about bad guesses
  stats4::mle(minuslogl = mixexplik, 
              start= list(p = 0.2,
                          lambda1 = as.numeric(fit1$estimate), 
                          lambda2 = as.numeric(fit1$estimate)), 
              method="Nelder-Mead")
)
get_prob <- function(x, rate1, rate2, p, x_scale, bin_width = 1){
  p1 <- pexp((x+bin_width)/x_scale, rate1) - pexp((x-bin_width)/x_scale, rate1)
  p2 <- pexp((x+bin_width)/x_scale, rate2) - pexp((x-bin_width)/x_scale, rate2)
  mixp <- p*p1+(1-p)*p2
  return(c(p1,p2,mixp))
}
x_scale <- 1e05
xstar <- round(x*x_scale)
probs <- sapply(xstar, FUN = get_prob, rate1 = mle_fit@coef["lambda1"], 
                rate2 = mle_fit@coef["lambda2"], p = mle_fit@coef["p"],
                x_scale = x_scale, bin_width = 1)
bound <- qexp(p = 0.5, rate = mle_fit@coef["p"]*mle_fit@coef["lambda1"])
probs_df <- t(log10(probs)) %>%
  as_tibble() %>%
  dplyr::rename(
    "log10prob_large" = V2,
    "log10prob_small" = V1,
    "log10prob_mix" = p
  ) %>%
  dplyr::mutate(
    incidence = x,
    outbreak_size = ifelse(incidence < bound, "small", "large"),
    plot_prob = ifelse(outbreak_size == "small", log10prob_small, log10prob_large)
  )
ggplot(probs_df, aes(x = incidence)) +
  geom_line(aes(y = log10prob_mix, color = "forestgreen")) +
  geom_point(aes(y = plot_prob, fill = outbreak_size), size = 3, shape = 21, color = "white") +
  geom_vline(aes(xintercept = bound), linetype = 2, color = "grey35") +
  labs(x = "Case incidence (reports/person)", 
       y = expression(paste(Log[10]," probability"))) +
  scale_fill_discrete(name = NULL, labels = c("Large outbreaks", "Small outbreaks")) +
  scale_color_manual(name = NULL, labels = "GMM fit", values = "forestgreen") +
  theme_minimal(base_size = 12)

OK, so now I can identify outbreak years in the untransformed (just cases) data for each city.

outbreak_ids <- cusum_data %>%
  dplyr::mutate(
    incidence = cases/pop,
    outbreak_year = ifelse(incidence < bound, FALSE, TRUE)
  ) %>%
  dplyr::select(region, year, outbreak_year)
measles_data <- measles_data %>%
  left_join(outbreak_ids)
Joining, by = c("region", "year")
ggplot(measles_data, aes(x = date, y = cases, color = outbreak_year, group = year)) +
  geom_line() +
  facet_wrap(~region, scales = "free")

Let’s look at a test case of Niamey post 1995 leading up to the outbreak in 1999.

niamey_test <- measles_data %>%
  filter(region == "Niamey (City)") %>%
  filter(year > 1998 & year < 2000)
cases <- niamey_test$cases
variance <- spaero::get_stats(
    x = cases,
    center_trend = "local_constant", 
    center_kernel = "uniform", 
    center_bandwidth = 16, 
    stat_trend = "local_constant", 
    stat_kernel = "uniform", 
    stat_bandwidth = 16, 
    lag = 1, 
    backward_only = TRUE
  )$stats$variance
plot_it(dates = niamey_test$date, cases = cases, ews = log(variance), lab = "log(variance)", title = NULL)

niamey_test <- measles_data %>%
  filter(region == "Niamey (City)") 
out_vars <- tibble()
for(do_year in unique(niamey_test$year)){
  tmp_cases <- niamey_test %>%
    filter(year == do_year) %>%
    pull(cases)
  
  tmp_stats <- spaero::get_stats(
    x = tmp_cases,
    center_trend = "local_constant", 
    center_kernel = "uniform", 
    center_bandwidth = 16, 
    stat_trend = "local_constant", 
    stat_kernel = "uniform", 
    stat_bandwidth = 16, 
    lag = 1, 
    backward_only = TRUE
  )$stats
  
  tmp_var <- tibble(
    year = do_year,
    date = filter(niamey_test, year == do_year)$date,
    variance = tmp_stats$variance,
    outbreak_year = filter(niamey_test, year == do_year)$outbreak_year
  )
  
  out_vars <- bind_rows(out_vars, tmp_var)
}
max_case_week <- niamey_test %>%
  group_by(year) %>%
  filter(cases == max(cases)) %>%
  filter(week_of_year == min(week_of_year))
ggplot() +
  geom_line(data = out_vars, aes(x = lubridate::week(date), y = log(variance), 
                                 group = year, color = outbreak_year)) +
  geom_vline(data = max_case_week, aes(xintercept = week_of_year, color = outbreak_year), linetype = 2)

Hmmm, this seems promising. Perhaps I can fit a GAM through the trajectories and use model selection to determine if an interaction model (outbreak year*time interaction effect) fits better than a GAM with no interaction. Then can use Gavin’s methods for determining time points that are different. Model-based minimum time of difference would be the on-average indicator time, which could be compared to max cases within the year. Should do this with data (like above) and the simulations.

gam_data <- out_vars %>%
  dplyr::select(date, variance, outbreak_year) %>%
  dplyr::mutate(
    week_of_year = lubridate::week(date),
    log_variance = log(variance+0.00001),
    outbreak_year = as.factor(outbreak_year)
  ) %>%
  dplyr::select(-date)
  
test_formula <- as.formula(
  log_variance ~ outbreak_year + s(week_of_year, by = outbreak_year)
)
gam_model <- mgcv::gam(formula = test_formula, data = gam_data)
pred_df <- tibble(
  week_of_year = rep(1:52, times = 2),
  outbreak_year = rep(as.factor(c(TRUE, FALSE)), each = 52)
)
preds <- predict(gam_model, newdata = pred_df, type = "response", se.fit = TRUE)
ests <- as.numeric(preds[[1]])
ses <- as.numeric(preds[[2]])
pred_df <- pred_df %>%
  mutate(preds = ests,
         se = ses)
ggplot(pred_df, aes(x = week_of_year, y = preds, color = outbreak_year)) +
  geom_line() +
  geom_line(aes(y = preds+(se*2)), linetype = 2) +
  geom_line(aes(y = preds-(se*2)), linetype = 2)

But first, look at just case trajectories

ggplot(niamey_test, aes(x = week_of_year, y = cases, color = outbreak_year, group = year)) +
  geom_line()

gam_data <- niamey_test %>%
  dplyr::select(date, cases, outbreak_year) %>%
  dplyr::mutate(
    week_of_year = lubridate::week(date),
    log_cases = log(cases+0.0001),
    outbreak_year = as.factor(outbreak_year)
  ) %>%
  dplyr::select(-date)
  
test_formula <- as.formula(
  log_cases ~ outbreak_year + s(week_of_year, by = outbreak_year)
)
gam_model <- mgcv::gam(formula = test_formula, data = gam_data)
pred_df <- tibble(
  week_of_year = rep(1:52, times = 2),
  outbreak_year = rep(as.factor(c(TRUE, FALSE)), each = 52)
)
preds <- predict(gam_model, newdata = pred_df, type = "response", se.fit = TRUE)
ests <- as.numeric(preds[[1]])
ses <- as.numeric(preds[[2]])
pred_df <- pred_df %>%
  mutate(preds = ests,
         se = ses)
ggplot(pred_df, aes(x = week_of_year, y = preds, color = outbreak_year)) +
  geom_line() +
  geom_line(aes(y = preds+(se*2)), linetype = 2) +
  geom_line(aes(y = preds-(se*2)), linetype = 2)

Extra

Maybe there is a variance value that serves as an indicator? Look at high AUCs from the simulations to see what the variances are.

ews <- read_csv("../../results/ews-emergence.csv") %>%
  filter(city == "Maradi" & metric == "variance" & half == "second")
Parsed with column specification:
cols(
  metric = col_character(),
  sim = col_integer(),
  value = col_double(),
  half = col_character(),
  city = col_character(),
  susc_discount = col_double()
)

|========                                                                                                                               |   6%
|=========                                                                                                                              |   6%
|=========                                                                                                                      |   7%    1 MB
|==========                                                                                                                     |   8%    1 MB
|===========                                                                                                                    |   8%    1 MB
|============                                                                                                                   |   9%    1 MB
|=============                                                                                                                  |  10%    1 MB
|==============                                                                                                                 |  11%    1 MB
|===============                                                                                                                |  11%    1 MB
|===============                                                                                                                |  12%    1 MB
|================                                                                                                               |  13%    1 MB
|=================                                                                                                              |  13%    1 MB
|==================                                                                                                             |  14%    1 MB
|===================                                                                                                            |  15%    2 MB
|====================                                                                                                           |  15%    2 MB
|=====================                                                                                                          |  16%    2 MB
|======================                                                                                                         |  17%    2 MB
|======================                                                                                                         |  17%    2 MB
|=======================                                                                                                        |  18%    2 MB
|========================                                                                                                       |  19%    2 MB
|=========================                                                                                                      |  20%    2 MB
|==========================                                                                                                     |  20%    2 MB
|===========================                                                                                                    |  21%    2 MB
|============================                                                                                                   |  22%    2 MB
|=============================                                                                                                  |  22%    3 MB
|==============================                                                                                                 |  23%    3 MB
|===============================                                                                                                |  24%    3 MB
|================================                                                                                               |  25%    3 MB
|================================                                                                                               |  25%    3 MB
|=================================                                                                                              |  26%    3 MB
|==================================                                                                                             |  27%    3 MB
|===================================                                                                                            |  27%    3 MB
|====================================                                                                                           |  28%    3 MB
|=====================================                                                                                          |  29%    3 MB
|======================================                                                                                         |  29%    4 MB
|=======================================                                                                                        |  30%    4 MB
|=======================================                                                                                        |  31%    4 MB
|========================================                                                                                       |  31%    4 MB
|=========================================                                                                                      |  32%    4 MB
|==========================================                                                                                     |  33%    4 MB
|===========================================                                                                                    |  34%    4 MB
|============================================                                                                                   |  34%    4 MB
|=============================================                                                                                  |  35%    4 MB
|==============================================                                                                                 |  36%    4 MB
|===============================================                                                                                |  36%    4 MB
|===============================================                                                                                |  37%    5 MB
|================================================                                                                               |  38%    5 MB
|=================================================                                                                              |  38%    5 MB
|==================================================                                                                             |  39%    5 MB
|===================================================                                                                            |  40%    5 MB
|====================================================                                                                           |  40%    5 MB
|=====================================================                                                                          |  41%    5 MB
|======================================================                                                                         |  42%    5 MB
|=======================================================                                                                        |  42%    5 MB
|=======================================================                                                                        |  43%    5 MB
|========================================================                                                                       |  44%    5 MB
|=========================================================                                                                      |  45%    6 MB
|==========================================================                                                                     |  45%    6 MB
|===========================================================                                                                    |  46%    6 MB
|============================================================                                                                   |  47%    6 MB
|=============================================================                                                                  |  47%    6 MB
|==============================================================                                                                 |  48%    6 MB
|===============================================================                                                                |  49%    6 MB
|================================================================                                                               |  50%    6 MB
|================================================================                                                               |  50%    6 MB
|=================================================================                                                              |  51%    6 MB
|==================================================================                                                             |  52%    7 MB
|===================================================================                                                            |  52%    7 MB
|====================================================================                                                           |  53%    7 MB
|=====================================================================                                                          |  54%    7 MB
|======================================================================                                                         |  54%    7 MB
|=======================================================================                                                        |  55%    7 MB
|=======================================================================                                                        |  56%    7 MB
|========================================================================                                                       |  56%    7 MB
|=========================================================================                                                      |  57%    7 MB
|==========================================================================                                                     |  58%    7 MB
|===========================================================================                                                    |  58%    7 MB
|============================================================================                                                   |  59%    8 MB
|=============================================================================                                                  |  60%    8 MB
|==============================================================================                                                 |  61%    8 MB
|===============================================================================                                                |  61%    8 MB
|===============================================================================                                                |  62%    8 MB
|================================================================================                                               |  63%    8 MB
|=================================================================================                                              |  63%    8 MB
|==================================================================================                                             |  64%    8 MB
|===================================================================================                                            |  65%    8 MB
|====================================================================================                                           |  65%    8 MB
|=====================================================================================                                          |  66%    8 MB
|======================================================================================                                         |  67%    9 MB
|======================================================================================                                         |  67%    9 MB
|=======================================================================================                                        |  68%    9 MB
|========================================================================================                                       |  69%    9 MB
|=========================================================================================                                      |  70%    9 MB
|==========================================================================================                                     |  70%    9 MB
|===========================================================================================                                    |  71%    9 MB
|============================================================================================                                   |  72%    9 MB
|=============================================================================================                                  |  72%    9 MB
|==============================================================================================                                 |  73%    9 MB
|===============================================================================================                                |  74%   10 MB
|================================================================================================                               |  75%   10 MB
|================================================================================================                               |  75%   10 MB
|=================================================================================================                              |  76%   10 MB
|==================================================================================================                             |  77%   10 MB
|===================================================================================================                            |  77%   10 MB
|====================================================================================================                           |  78%   10 MB
|=====================================================================================================                          |  79%   10 MB
|======================================================================================================                         |  79%   10 MB
|=======================================================================================================                        |  80%   10 MB
|=======================================================================================================                        |  81%   10 MB
|========================================================================================================                       |  81%   11 MB
|=========================================================================================================                      |  82%   11 MB
|==========================================================================================================                     |  83%   11 MB
|===========================================================================================================                    |  84%   11 MB
|============================================================================================================                   |  84%   11 MB
|=============================================================================================================                  |  85%   11 MB
|==============================================================================================================                 |  86%   11 MB
|===============================================================================================================                |  86%   11 MB
|===============================================================================================================                |  87%   11 MB
|================================================================================================================               |  88%   11 MB
|=================================================================================================================              |  88%   12 MB
|==================================================================================================================             |  89%   12 MB
|===================================================================================================================            |  90%   12 MB
|====================================================================================================================           |  90%   12 MB
|=====================================================================================================================          |  91%   12 MB
|======================================================================================================================         |  92%   12 MB
|======================================================================================================================         |  92%   12 MB
|=======================================================================================================================        |  93%   12 MB
|========================================================================================================================       |  94%   12 MB
|=========================================================================================================================      |  95%   12 MB
|==========================================================================================================================     |  95%   12 MB
|===========================================================================================================================    |  96%   13 MB
|============================================================================================================================   |  97%   13 MB
|=============================================================================================================================  |  97%   13 MB
|============================================================================================================================== |  98%   13 MB
|===============================================================================================================================|  99%   13 MB
|================================================================================================================================| 100%   13 MB
|================================================================================================================================| 100%   13 MB
ggplot(ews, aes(x = value)) +
  geom_histogram() +
  facet_wrap(~susc_discount, scales = "free")

med_var <- ews %>%
  group_by(susc_discount) %>%
  summarise(value = median(value))
LS0tCnRpdGxlOiAiRVdTIGluIG1lYXNsZXMgZGF0YSIKb3V0cHV0OiBodG1sX25vdGVib29rCmRhdGU6IDIwMTktMDEtMjIKLS0tCgpJIHVzZSB0aGUgYHNwYWVybzo6Z2V0X3N0YXRzKClgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBFV1Mgb3ZlciBhIG1vdmluZyB3aW5kb3cgZm9yIHRoZSBtZWFzbGVzIGNhc2UgaW5jaWRlbmNlIGRhdGEgZnJvbSBmb3VyIGNpdGllcyBpbiBOaWdlci4KSSB1c2UgYSBiYW5kd2lkdGggb2YgMzUgd2Vla3MsIHJlc3VsdGluZyBpbiBhIGByIDM1KjJgIHdlZWsgd2luZG93LgpBbGwgRVdTIGFyZSBjYWxjdWxhdGVkIHdpdGggYGJhY2t3YXJkX29ubHkgPSBUUlVFYCBzbyB0aGF0IEVXUyBhcmUgb25seSBjYWxjdWxhdGVkIGJhc2VkIG9uIGRhdGEgaW4gdGhlIHBhc3QuCgpgYGB7ciBjYWxjLWV3c30KIyBMb2FkIHBhY2thZ2VzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzcGFlcm8pCgoKIyBMb2FkIGRhdGEgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpmbmFtZSA8LSAiLi4vLi4vZGF0YS9jbGVhbi1kYXRhL3dlZWtseS1tZWFzbGVzLWluY2lkZW5jZS1uaWdlci1jaXRpZXMtY2xlYW4uUkRTIgptZWFzbGVzX2RhdGEgPC0gcmVhZFJEUyhmbmFtZSkgJT4lCiAgZmlsdGVyKHllYXIgPiAxOTk0KSAgIyBkcm9wIGZpcnN0IE5BIHllYXIsIG9ubHkgdXNlZCBmb3IgbW9kZWxpbmcKCgojIENhbGN1bGF0ZSBFV1MgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmFsbF9zdGF0cyA8LSB0aWJibGUoKSAgIyBlbXB0eSB0aWJibGUgZm9yIHN0b3JhZ2UKCmZvcihkb19yZWdpb24gaW4gdW5pcXVlKG1lYXNsZXNfZGF0YSRyZWdpb24pKXsKICAKICBjYXNlcyA8LSBtZWFzbGVzX2RhdGEgJT4lCiAgICBmaWx0ZXIocmVnaW9uID09IGRvX3JlZ2lvbikgJT4lCiAgICBwdWxsKGNhc2VzKQogIAogIGNpdHlfc3RhdHMgPC0gc3BhZXJvOjpnZXRfc3RhdHMoCiAgICB4ID0gY2FzZXMsCiAgICBjZW50ZXJfdHJlbmQgPSAibG9jYWxfY29uc3RhbnQiLCAKICAgIGNlbnRlcl9rZXJuZWwgPSAidW5pZm9ybSIsIAogICAgY2VudGVyX2JhbmR3aWR0aCA9IDM1LCAKICAgIHN0YXRfdHJlbmQgPSAibG9jYWxfY29uc3RhbnQiLCAKICAgIHN0YXRfa2VybmVsID0gInVuaWZvcm0iLCAKICAgIHN0YXRfYmFuZHdpZHRoID0gMzUsIAogICAgbGFnID0gMSwgCiAgICBiYWNrd2FyZF9vbmx5ID0gVFJVRQogICkkc3RhdHMKICAKICBjaXR5X3N0YXRzX3RiIDwtIGFzX3RpYmJsZShjaXR5X3N0YXRzKSAlPiUKICAgIG11dGF0ZSgKICAgICAgdGltZV9pdGVyID0gMTpuKCksCiAgICAgIGRhdGUgPSB1bmlxdWUobWVhc2xlc19kYXRhJGRhdGUpCiAgICApICU+JQogICAgZ2F0aGVyKGtleSA9IGV3cywgdmFsdWUgPSB2YWx1ZSwgLXRpbWVfaXRlciwgLWRhdGUpICU+JQogICAgbXV0YXRlKHJlZ2lvbiA9IGRvX3JlZ2lvbikKICAKICBhbGxfc3RhdHMgPC0gYmluZF9yb3dzKGFsbF9zdGF0cywgY2l0eV9zdGF0c190YikKfQoKYGBgCgpEZWZpbmUgYSBmdW5jdGlvbiB0byBwbG90IHRoZSBFV1MgYW5kIHRoZSBvYnNlcnZhdGlvbnMuCgpgYGB7ciBwbG90LWV3cy1mdW5jfQpwbG90X2l0IDwtIGZ1bmN0aW9uKGRhdGVzLCBjYXNlcywgZXdzLCBsYWIsIHRpdGxlKXsKICBwYXIobWFyID0gYyg1LDUsMiw1KSkKICBwbG90KGRhdGVzLCBjYXNlcywgdHlwZSA9ICJsIiwgbWFpbiA9IHRpdGxlKQogIHBhcihuZXcgPSBUKQogIHBsb3QoZGF0ZXMsIGV3cywgdHlwZSA9ICJsIiwgY29sID0gInJlZCIsIGF4ZXM9RiwgeGxhYj1OQSwgeWxhYj1OQSkKICBheGlzKHNpZGUgPSA0KQogIG10ZXh0KHNpZGUgPSA0LCBsaW5lID0gMywgbGFiLCBjZXggPSAwLjcpCn0KYGBgCgojIyMgQWdhZGV6CgpgYGB7ciBhZ2FkZXosIGZpZy5oZWlnaHQ9OH0KZG9fcmVnaW9uIDwtICJBZ2FkZXogKENpdHkpIgpkYXRlcyA8LSB1bmlxdWUobWVhc2xlc19kYXRhJGRhdGUpIApwYXIobWZyb3cgPSBjKDQsMykpCmZvcihkb19ld3MgaW4gdW5pcXVlKGFsbF9zdGF0cyRld3MpKXsKICBjYXNlcyA8LSBmaWx0ZXIobWVhc2xlc19kYXRhLCByZWdpb24gPT0gZG9fcmVnaW9uKSAlPiUgcHVsbChjYXNlcykKICBld3MgPC0gZmlsdGVyKGFsbF9zdGF0cywgcmVnaW9uID09IGRvX3JlZ2lvbiAmIGV3cyA9PSBkb19ld3MpICU+JSBwdWxsKHZhbHVlKQogIHBsb3RfaXQoZGF0ZXMsIGNhc2VzLCBld3MsIGxhYiA9ICJld3MiLCB0aXRsZSA9IGRvX2V3cykKfQpgYGAKCiMjIyBNYXJhZGkKYGBge3IgbWFyYWRpLCBmaWcuaGVpZ2h0PTh9CmRvX3JlZ2lvbiA8LSAiTWFyYWRpIChDaXR5KSIKZGF0ZXMgPC0gdW5pcXVlKG1lYXNsZXNfZGF0YSRkYXRlKSAKcGFyKG1mcm93ID0gYyg0LDMpKQpmb3IoZG9fZXdzIGluIHVuaXF1ZShhbGxfc3RhdHMkZXdzKSl7CiAgY2FzZXMgPC0gZmlsdGVyKG1lYXNsZXNfZGF0YSwgcmVnaW9uID09IGRvX3JlZ2lvbikgJT4lIHB1bGwoY2FzZXMpCiAgZXdzIDwtIGZpbHRlcihhbGxfc3RhdHMsIHJlZ2lvbiA9PSBkb19yZWdpb24gJiBld3MgPT0gZG9fZXdzKSAlPiUgcHVsbCh2YWx1ZSkKICBwbG90X2l0KGRhdGVzLCBjYXNlcywgZXdzLCBsYWIgPSAiZXdzIiwgdGl0bGUgPSBkb19ld3MpCn0KYGBgCgojIyMgTmlhbWV5CmBgYHtyIG5pYW1leSwgZmlnLmhlaWdodD04fQpkb19yZWdpb24gPC0gIk5pYW1leSAoQ2l0eSkiCmRhdGVzIDwtIHVuaXF1ZShtZWFzbGVzX2RhdGEkZGF0ZSkgCnBhcihtZnJvdyA9IGMoNCwzKSkKZm9yKGRvX2V3cyBpbiB1bmlxdWUoYWxsX3N0YXRzJGV3cykpewogIGNhc2VzIDwtIGZpbHRlcihtZWFzbGVzX2RhdGEsIHJlZ2lvbiA9PSBkb19yZWdpb24pICU+JSBwdWxsKGNhc2VzKQogIGV3cyA8LSBmaWx0ZXIoYWxsX3N0YXRzLCByZWdpb24gPT0gZG9fcmVnaW9uICYgZXdzID09IGRvX2V3cykgJT4lIHB1bGwodmFsdWUpCiAgcGxvdF9pdChkYXRlcywgY2FzZXMsIGV3cywgbGFiID0gImV3cyIsIHRpdGxlID0gZG9fZXdzKQp9CmBgYAoKIyMjIFppbmRlcgpgYGB7ciB6aW5kZXIsIGZpZy5oZWlnaHQ9OH0KZG9fcmVnaW9uIDwtICJaaW5kZXIgKENpdHkpIgpkYXRlcyA8LSB1bmlxdWUobWVhc2xlc19kYXRhJGRhdGUpIApwYXIobWZyb3cgPSBjKDQsMykpCmZvcihkb19ld3MgaW4gdW5pcXVlKGFsbF9zdGF0cyRld3MpKXsKICBjYXNlcyA8LSBmaWx0ZXIobWVhc2xlc19kYXRhLCByZWdpb24gPT0gZG9fcmVnaW9uKSAlPiUgcHVsbChjYXNlcykKICBld3MgPC0gZmlsdGVyKGFsbF9zdGF0cywgcmVnaW9uID09IGRvX3JlZ2lvbiAmIGV3cyA9PSBkb19ld3MpICU+JSBwdWxsKHZhbHVlKQogIHBsb3RfaXQoZGF0ZXMsIGNhc2VzLCBld3MsIGxhYiA9ICJld3MiLCB0aXRsZSA9IGRvX2V3cykKfQpgYGAKCiMgUG9zdC1vdXRicmVhayBFV1MKCkFzIHRoZSBwbG90cyBhYm92ZSBzaG93LCBtb3N0IEVXUyBnZXQgc3dhbXBlZCBvdXQgYnkgdGhlICJtZW1vcnkiIG9mIG91dGJyZWFrcy4KVG8gdHJ5IGFuZCBnZXQgYXJvdW5kIHRoaXMsIEkgYW0gZ29pbmcgdG8gbG9vayBhdCBFV1MgYWZ0ZXIgcmVzZXR0aW5nIHRoZW0gZm9sbG93aW5nIG91dGJyZWFrcy4KSSBkb24ndCB0aGluayB0aGlzIGlzIHRvbyBjb250cml2ZWQgYmVjYXVzZSByZWFsLXdvcmxkIGVhcmx5IGRldGVjdGlvbiBzeXN0ZW1zIHdvdWxkIGhhdmUgdG8gZG8gdGhlIHNhbWUgdGhpbmcuCgojIyBIb3cgdG8gSUQgb3V0YnJlYWtzCkZvbGxvd2luZyBUb2J5J3Mgc3VnZ2VzdGlvbiwgSSBhbSBnb2luZyB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygb3V0YnJlYWsgc2l6ZXMgKGkuZS4sIGN1bXVsYXRpdmUgY2FzZXMgaW4gZWFjaCB5ZWFyKS4KSSBjYW4gdGhlbiBmaXQgYSBtaXh0dXJlIGRpc3RyaWJ1dGlvbiBvZiB0d28gd2VpZ2h0ZWQgZXhwb25lbnRpYWxzIHRvIHRoZSBkYXRhLCB3aXRoIG9uZSBleHBvbmVudGlhbCBleHBsYWluaW5nIHRoZSBsYXJnZSBvdXRicmVha3MgYW5kIG9uZSB0aGUgc21hbGwgb3V0YnJlYWtzLgpUaGlzIHByb3ZpZGVzIGFuIG9iamVjdGl2ZSB3YXkgdG8gZGVmaW5lIG91dGJyZWFrcyBmb3IgRVdTIGRldGVjdGlvbi4KCmBgYHtyfQpwb3BfZGF0YSA8LSB0aWJibGUoCiAgcmVnaW9uID0gYygiQWdhZGV6IChDaXR5KSIsICJNYXJhZGkgKENpdHkpIiwgIk5pYW1leSAoQ2l0eSkiLCAiWmluZGVyIChDaXR5KSIpLAogIHBvcCA9IGMoMTE4MjI0LCAyNjcyNDksIDEwMjcwMDAsIDMyMjkzNSkKKQoKY3VzdW1fZGF0YSA8LSBtZWFzbGVzX2RhdGEgJT4lCiAgZ3JvdXBfYnkocmVnaW9uLCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUKICBsZWZ0X2pvaW4ocG9wX2RhdGEpCgpnZ3Bsb3QoY3VzdW1fZGF0YSwgYWVzKHggPSBjYXNlcy9wb3ApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSByZWdpb24pKQoKY3VzdW1fY2FzZXMgPC0gc29ydChjdXN1bV9kYXRhJGNhc2VzIC8gY3VzdW1fZGF0YSRwb3ApCmZpdDEgPC0gTUFTUzo6Zml0ZGlzdHIoY3VzdW1fY2FzZXMsICJleHBvbmVudGlhbCIpIAoKeCA8LSBjdXN1bV9jYXNlcwptaXhleHBsaWsgPC0gZnVuY3Rpb24ocCxsYW1iZGExLGxhbWJkYTIpIHsKICB6IDwtIHAqZGV4cCh4LGxhbWJkYTEpICsgKDEtcCkqZGV4cCh4LGxhbWJkYTIpCiAgcmV0dXJuKC1zdW0obG9nKHopKSkKfQoKbWxlX2ZpdCA8LSBzdXBwcmVzc1dhcm5pbmdzKCAgIyBpZ25vcmUgd2FybmluZ3MgYWJvdXQgYmFkIGd1ZXNzZXMKICBzdGF0czQ6Om1sZShtaW51c2xvZ2wgPSBtaXhleHBsaWssIAogICAgICAgICAgICAgIHN0YXJ0PSBsaXN0KHAgPSAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhMSA9IGFzLm51bWVyaWMoZml0MSRlc3RpbWF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYTIgPSBhcy5udW1lcmljKGZpdDEkZXN0aW1hdGUpKSwgCiAgICAgICAgICAgICAgbWV0aG9kPSJOZWxkZXItTWVhZCIpCikKCmdldF9wcm9iIDwtIGZ1bmN0aW9uKHgsIHJhdGUxLCByYXRlMiwgcCwgeF9zY2FsZSwgYmluX3dpZHRoID0gMSl7CiAgcDEgPC0gcGV4cCgoeCtiaW5fd2lkdGgpL3hfc2NhbGUsIHJhdGUxKSAtIHBleHAoKHgtYmluX3dpZHRoKS94X3NjYWxlLCByYXRlMSkKICBwMiA8LSBwZXhwKCh4K2Jpbl93aWR0aCkveF9zY2FsZSwgcmF0ZTIpIC0gcGV4cCgoeC1iaW5fd2lkdGgpL3hfc2NhbGUsIHJhdGUyKQogIG1peHAgPC0gcCpwMSsoMS1wKSpwMgogIHJldHVybihjKHAxLHAyLG1peHApKQp9Cgp4X3NjYWxlIDwtIDFlMDUKeHN0YXIgPC0gcm91bmQoeCp4X3NjYWxlKQpwcm9icyA8LSBzYXBwbHkoeHN0YXIsIEZVTiA9IGdldF9wcm9iLCByYXRlMSA9IG1sZV9maXRAY29lZlsibGFtYmRhMSJdLCAKICAgICAgICAgICAgICAgIHJhdGUyID0gbWxlX2ZpdEBjb2VmWyJsYW1iZGEyIl0sIHAgPSBtbGVfZml0QGNvZWZbInAiXSwKICAgICAgICAgICAgICAgIHhfc2NhbGUgPSB4X3NjYWxlLCBiaW5fd2lkdGggPSAxKQoKYm91bmQgPC0gcWV4cChwID0gMC41LCByYXRlID0gbWxlX2ZpdEBjb2VmWyJwIl0qbWxlX2ZpdEBjb2VmWyJsYW1iZGExIl0pCgpwcm9ic19kZiA8LSB0KGxvZzEwKHByb2JzKSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgZHBseXI6OnJlbmFtZSgKICAgICJsb2cxMHByb2JfbGFyZ2UiID0gVjIsCiAgICAibG9nMTBwcm9iX3NtYWxsIiA9IFYxLAogICAgImxvZzEwcHJvYl9taXgiID0gcAogICkgJT4lCiAgZHBseXI6Om11dGF0ZSgKICAgIGluY2lkZW5jZSA9IHgsCiAgICBvdXRicmVha19zaXplID0gaWZlbHNlKGluY2lkZW5jZSA8IGJvdW5kLCAic21hbGwiLCAibGFyZ2UiKSwKICAgIHBsb3RfcHJvYiA9IGlmZWxzZShvdXRicmVha19zaXplID09ICJzbWFsbCIsIGxvZzEwcHJvYl9zbWFsbCwgbG9nMTBwcm9iX2xhcmdlKQogICkKCmdncGxvdChwcm9ic19kZiwgYWVzKHggPSBpbmNpZGVuY2UpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbG9nMTBwcm9iX21peCwgY29sb3IgPSAiZm9yZXN0Z3JlZW4iKSkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBwbG90X3Byb2IsIGZpbGwgPSBvdXRicmVha19zaXplKSwgc2l6ZSA9IDMsIHNoYXBlID0gMjEsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBib3VuZCksIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JleTM1IikgKwogIGxhYnMoeCA9ICJDYXNlIGluY2lkZW5jZSAocmVwb3J0cy9wZXJzb24pIiwgCiAgICAgICB5ID0gZXhwcmVzc2lvbihwYXN0ZShMb2dbMTBdLCIgcHJvYmFiaWxpdHkiKSkpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSBOVUxMLCBsYWJlbHMgPSBjKCJMYXJnZSBvdXRicmVha3MiLCAiU21hbGwgb3V0YnJlYWtzIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9IE5VTEwsIGxhYmVscyA9ICJHTU0gZml0IiwgdmFsdWVzID0gImZvcmVzdGdyZWVuIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCgpgYGAKCk9LLCBzbyBub3cgSSBjYW4gaWRlbnRpZnkgb3V0YnJlYWsgeWVhcnMgaW4gdGhlIHVudHJhbnNmb3JtZWQgKGp1c3QgY2FzZXMpIGRhdGEgZm9yIGVhY2ggY2l0eS4KCmBgYHtyfQpvdXRicmVha19pZHMgPC0gY3VzdW1fZGF0YSAlPiUKICBkcGx5cjo6bXV0YXRlKAogICAgaW5jaWRlbmNlID0gY2FzZXMvcG9wLAogICAgb3V0YnJlYWtfeWVhciA9IGlmZWxzZShpbmNpZGVuY2UgPCBib3VuZCwgRkFMU0UsIFRSVUUpCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KHJlZ2lvbiwgeWVhciwgb3V0YnJlYWtfeWVhcikKCm1lYXNsZXNfZGF0YSA8LSBtZWFzbGVzX2RhdGEgJT4lCiAgbGVmdF9qb2luKG91dGJyZWFrX2lkcykKCmdncGxvdChtZWFzbGVzX2RhdGEsIGFlcyh4ID0gZGF0ZSwgeSA9IGNhc2VzLCBjb2xvciA9IG91dGJyZWFrX3llYXIsIGdyb3VwID0geWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfd3JhcCh+cmVnaW9uLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKTGV0J3MgbG9vayBhdCBhIHRlc3QgY2FzZSBvZiBOaWFtZXkgcG9zdCAxOTk1IGxlYWRpbmcgdXAgdG8gdGhlIG91dGJyZWFrIGluIDE5OTkuCgpgYGB7cn0KbmlhbWV5X3Rlc3QgPC0gbWVhc2xlc19kYXRhICU+JQogIGZpbHRlcihyZWdpb24gPT0gIk5pYW1leSAoQ2l0eSkiKSAlPiUKICBmaWx0ZXIoeWVhciA+IDE5OTggJiB5ZWFyIDwgMjAwMCkKCmNhc2VzIDwtIG5pYW1leV90ZXN0JGNhc2VzCnZhcmlhbmNlIDwtIHNwYWVybzo6Z2V0X3N0YXRzKAogICAgeCA9IGNhc2VzLAogICAgY2VudGVyX3RyZW5kID0gImxvY2FsX2NvbnN0YW50IiwgCiAgICBjZW50ZXJfa2VybmVsID0gInVuaWZvcm0iLCAKICAgIGNlbnRlcl9iYW5kd2lkdGggPSAxNiwgCiAgICBzdGF0X3RyZW5kID0gImxvY2FsX2NvbnN0YW50IiwgCiAgICBzdGF0X2tlcm5lbCA9ICJ1bmlmb3JtIiwgCiAgICBzdGF0X2JhbmR3aWR0aCA9IDE2LCAKICAgIGxhZyA9IDEsIAogICAgYmFja3dhcmRfb25seSA9IFRSVUUKICApJHN0YXRzJHZhcmlhbmNlCgpwbG90X2l0KGRhdGVzID0gbmlhbWV5X3Rlc3QkZGF0ZSwgY2FzZXMgPSBjYXNlcywgZXdzID0gbG9nKHZhcmlhbmNlKSwgbGFiID0gImxvZyh2YXJpYW5jZSkiLCB0aXRsZSA9IE5VTEwpCgpgYGAKCmBgYHtyfQpuaWFtZXlfdGVzdCA8LSBtZWFzbGVzX2RhdGEgJT4lCiAgZmlsdGVyKHJlZ2lvbiA9PSAiTmlhbWV5IChDaXR5KSIpIAoKb3V0X3ZhcnMgPC0gdGliYmxlKCkKCmZvcihkb195ZWFyIGluIHVuaXF1ZShuaWFtZXlfdGVzdCR5ZWFyKSl7CiAgdG1wX2Nhc2VzIDwtIG5pYW1leV90ZXN0ICU+JQogICAgZmlsdGVyKHllYXIgPT0gZG9feWVhcikgJT4lCiAgICBwdWxsKGNhc2VzKQogIAogIHRtcF9zdGF0cyA8LSBzcGFlcm86OmdldF9zdGF0cygKICAgIHggPSB0bXBfY2FzZXMsCiAgICBjZW50ZXJfdHJlbmQgPSAibG9jYWxfY29uc3RhbnQiLCAKICAgIGNlbnRlcl9rZXJuZWwgPSAidW5pZm9ybSIsIAogICAgY2VudGVyX2JhbmR3aWR0aCA9IDE2LCAKICAgIHN0YXRfdHJlbmQgPSAibG9jYWxfY29uc3RhbnQiLCAKICAgIHN0YXRfa2VybmVsID0gInVuaWZvcm0iLCAKICAgIHN0YXRfYmFuZHdpZHRoID0gMTYsIAogICAgbGFnID0gMSwgCiAgICBiYWNrd2FyZF9vbmx5ID0gVFJVRQogICkkc3RhdHMKICAKICB0bXBfdmFyIDwtIHRpYmJsZSgKICAgIHllYXIgPSBkb195ZWFyLAogICAgZGF0ZSA9IGZpbHRlcihuaWFtZXlfdGVzdCwgeWVhciA9PSBkb195ZWFyKSRkYXRlLAogICAgdmFyaWFuY2UgPSB0bXBfc3RhdHMkdmFyaWFuY2UsCiAgICBvdXRicmVha195ZWFyID0gZmlsdGVyKG5pYW1leV90ZXN0LCB5ZWFyID09IGRvX3llYXIpJG91dGJyZWFrX3llYXIKICApCiAgCiAgb3V0X3ZhcnMgPC0gYmluZF9yb3dzKG91dF92YXJzLCB0bXBfdmFyKQp9CgptYXhfY2FzZV93ZWVrIDwtIG5pYW1leV90ZXN0ICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIGZpbHRlcihjYXNlcyA9PSBtYXgoY2FzZXMpKSAlPiUKICBmaWx0ZXIod2Vla19vZl95ZWFyID09IG1pbih3ZWVrX29mX3llYXIpKQoKZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gb3V0X3ZhcnMsIGFlcyh4ID0gbHVicmlkYXRlOjp3ZWVrKGRhdGUpLCB5ID0gbG9nKHZhcmlhbmNlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0geWVhciwgY29sb3IgPSBvdXRicmVha195ZWFyKSkgKwogIGdlb21fdmxpbmUoZGF0YSA9IG1heF9jYXNlX3dlZWssIGFlcyh4aW50ZXJjZXB0ID0gd2Vla19vZl95ZWFyLCBjb2xvciA9IG91dGJyZWFrX3llYXIpLCBsaW5ldHlwZSA9IDIpCmBgYAoKSG1tbSwgdGhpcyBzZWVtcyBwcm9taXNpbmcuClBlcmhhcHMgSSBjYW4gZml0IGEgR0FNIHRocm91Z2ggdGhlIHRyYWplY3RvcmllcyBhbmQgdXNlIG1vZGVsIHNlbGVjdGlvbiB0byBkZXRlcm1pbmUgaWYgYW4gaW50ZXJhY3Rpb24gbW9kZWwgKG91dGJyZWFrIHllYXIqdGltZSBpbnRlcmFjdGlvbiBlZmZlY3QpIGZpdHMgYmV0dGVyIHRoYW4gYSBHQU0gd2l0aCBubyBpbnRlcmFjdGlvbi4KVGhlbiBjYW4gdXNlIEdhdmluJ3MgbWV0aG9kcyBmb3IgZGV0ZXJtaW5pbmcgdGltZSBwb2ludHMgdGhhdCBhcmUgZGlmZmVyZW50LgpNb2RlbC1iYXNlZCBtaW5pbXVtIHRpbWUgb2YgZGlmZmVyZW5jZSB3b3VsZCBiZSB0aGUgb24tYXZlcmFnZSBpbmRpY2F0b3IgdGltZSwgd2hpY2ggY291bGQgYmUgY29tcGFyZWQgdG8gbWF4IGNhc2VzIHdpdGhpbiB0aGUgeWVhci4KU2hvdWxkIGRvIHRoaXMgd2l0aCBkYXRhIChsaWtlIGFib3ZlKSBhbmQgdGhlIHNpbXVsYXRpb25zLgoKYGBge3J9CmdhbV9kYXRhIDwtIG91dF92YXJzICU+JQogIGRwbHlyOjpzZWxlY3QoZGF0ZSwgdmFyaWFuY2UsIG91dGJyZWFrX3llYXIpICU+JQogIGRwbHlyOjptdXRhdGUoCiAgICB3ZWVrX29mX3llYXIgPSBsdWJyaWRhdGU6OndlZWsoZGF0ZSksCiAgICBsb2dfdmFyaWFuY2UgPSBsb2codmFyaWFuY2UrMC4wMDAwMSksCiAgICBvdXRicmVha195ZWFyID0gYXMuZmFjdG9yKG91dGJyZWFrX3llYXIpCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1kYXRlKQogIAp0ZXN0X2Zvcm11bGEgPC0gYXMuZm9ybXVsYSgKICBsb2dfdmFyaWFuY2UgfiBvdXRicmVha195ZWFyICsgcyh3ZWVrX29mX3llYXIsIGJ5ID0gb3V0YnJlYWtfeWVhcikKKQpnYW1fbW9kZWwgPC0gbWdjdjo6Z2FtKGZvcm11bGEgPSB0ZXN0X2Zvcm11bGEsIGRhdGEgPSBnYW1fZGF0YSkKCnByZWRfZGYgPC0gdGliYmxlKAogIHdlZWtfb2ZfeWVhciA9IHJlcCgxOjUyLCB0aW1lcyA9IDIpLAogIG91dGJyZWFrX3llYXIgPSByZXAoYXMuZmFjdG9yKGMoVFJVRSwgRkFMU0UpKSwgZWFjaCA9IDUyKQopCgpwcmVkcyA8LSBwcmVkaWN0KGdhbV9tb2RlbCwgbmV3ZGF0YSA9IHByZWRfZGYsIHR5cGUgPSAicmVzcG9uc2UiLCBzZS5maXQgPSBUUlVFKQplc3RzIDwtIGFzLm51bWVyaWMocHJlZHNbWzFdXSkKc2VzIDwtIGFzLm51bWVyaWMocHJlZHNbWzJdXSkKcHJlZF9kZiA8LSBwcmVkX2RmICU+JQogIG11dGF0ZShwcmVkcyA9IGVzdHMsCiAgICAgICAgIHNlID0gc2VzKQoKZ2dwbG90KHByZWRfZGYsIGFlcyh4ID0gd2Vla19vZl95ZWFyLCB5ID0gcHJlZHMsIGNvbG9yID0gb3V0YnJlYWtfeWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZHMrKHNlKjIpKSwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZHMtKHNlKjIpKSwgbGluZXR5cGUgPSAyKQpgYGAKCiMgQnV0IGZpcnN0LCBsb29rIGF0IGp1c3QgY2FzZSB0cmFqZWN0b3JpZXMKCmBgYHtyfQpnZ3Bsb3QobmlhbWV5X3Rlc3QsIGFlcyh4ID0gd2Vla19vZl95ZWFyLCB5ID0gY2FzZXMsIGNvbG9yID0gb3V0YnJlYWtfeWVhciwgZ3JvdXAgPSB5ZWFyKSkgKwogIGdlb21fbGluZSgpCgpnYW1fZGF0YSA8LSBuaWFtZXlfdGVzdCAlPiUKICBkcGx5cjo6c2VsZWN0KGRhdGUsIGNhc2VzLCBvdXRicmVha195ZWFyKSAlPiUKICBkcGx5cjo6bXV0YXRlKAogICAgd2Vla19vZl95ZWFyID0gbHVicmlkYXRlOjp3ZWVrKGRhdGUpLAogICAgbG9nX2Nhc2VzID0gbG9nKGNhc2VzKzAuMDAwMSksCiAgICBvdXRicmVha195ZWFyID0gYXMuZmFjdG9yKG91dGJyZWFrX3llYXIpCiAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1kYXRlKQogIAp0ZXN0X2Zvcm11bGEgPC0gYXMuZm9ybXVsYSgKICBsb2dfY2FzZXMgfiBvdXRicmVha195ZWFyICsgcyh3ZWVrX29mX3llYXIsIGJ5ID0gb3V0YnJlYWtfeWVhcikKKQpnYW1fbW9kZWwgPC0gbWdjdjo6Z2FtKGZvcm11bGEgPSB0ZXN0X2Zvcm11bGEsIGRhdGEgPSBnYW1fZGF0YSkKCnByZWRfZGYgPC0gdGliYmxlKAogIHdlZWtfb2ZfeWVhciA9IHJlcCgxOjUyLCB0aW1lcyA9IDIpLAogIG91dGJyZWFrX3llYXIgPSByZXAoYXMuZmFjdG9yKGMoVFJVRSwgRkFMU0UpKSwgZWFjaCA9IDUyKQopCgpwcmVkcyA8LSBwcmVkaWN0KGdhbV9tb2RlbCwgbmV3ZGF0YSA9IHByZWRfZGYsIHR5cGUgPSAicmVzcG9uc2UiLCBzZS5maXQgPSBUUlVFKQplc3RzIDwtIGFzLm51bWVyaWMocHJlZHNbWzFdXSkKc2VzIDwtIGFzLm51bWVyaWMocHJlZHNbWzJdXSkKcHJlZF9kZiA8LSBwcmVkX2RmICU+JQogIG11dGF0ZShwcmVkcyA9IGVzdHMsCiAgICAgICAgIHNlID0gc2VzKQoKZ2dwbG90KHByZWRfZGYsIGFlcyh4ID0gd2Vla19vZl95ZWFyLCB5ID0gcHJlZHMsIGNvbG9yID0gb3V0YnJlYWtfeWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZHMrKHNlKjIpKSwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZHMtKHNlKjIpKSwgbGluZXR5cGUgPSAyKQpgYGAKCgoKIyBFeHRyYQpNYXliZSB0aGVyZSBpcyBhIHZhcmlhbmNlIHZhbHVlIHRoYXQgc2VydmVzIGFzIGFuIGluZGljYXRvcj8KTG9vayBhdCBoaWdoIEFVQ3MgZnJvbSB0aGUgc2ltdWxhdGlvbnMgdG8gc2VlIHdoYXQgdGhlIHZhcmlhbmNlcyBhcmUuCgpgYGB7cn0KZXdzIDwtIHJlYWRfY3N2KCIuLi8uLi9yZXN1bHRzL2V3cy1lbWVyZ2VuY2UuY3N2IikgJT4lCiAgZmlsdGVyKGNpdHkgPT0gIk1hcmFkaSIgJiBtZXRyaWMgPT0gInZhcmlhbmNlIiAmIGhhbGYgPT0gInNlY29uZCIpCgpnZ3Bsb3QoZXdzLCBhZXMoeCA9IHZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAofnN1c2NfZGlzY291bnQsIHNjYWxlcyA9ICJmcmVlIikKCm1lZF92YXIgPC0gZXdzICU+JQogIGdyb3VwX2J5KHN1c2NfZGlzY291bnQpICU+JQogIHN1bW1hcmlzZSh2YWx1ZSA9IG1lZGlhbih2YWx1ZSkpCmBgYA==